package service

import (
	"context"
	"errors"
	"gorm.io/gorm"
	"shop-bean/constant"
	"shop-bean/model"
	"shop-bean/request"
	"shop-common/library/variables"
	"shop-common/utils/pagehelper"
	"shop-common/utils/queryWrapper"
	"shop-service/repository"
)

type ITransportService interface {
	FindById(ctx context.Context, id uint) (*model.Transport, error)
	Find(ctx context.Context, shopId uint) ([]*model.Transport, error)
	FindPage(ctx context.Context, paginator *pagehelper.Paginator) (*pagehelper.Record, error)
	Create(ctx context.Context, transport *request.TransportRequest) (uint, error)
	Updates(ctx context.Context, transport *request.TransportRequest) (uint, error)
	CheckExist(ctx context.Context, transName string) (bool, error)
	DeleteById(ctx context.Context, transportId uint) error
	FindTransfee(ctx context.Context, transportId uint) (transfees []*model.Transfee, err error)
	FindFransfeeFree(ctx context.Context, transportId uint) (transfeeFrees []*model.TransfeeFree, err error)
}

type TransportService struct {
	db                     *gorm.DB
	repository             repository.ITransportRepository
	transfeeRepository     repository.ITransfeeRepository
	transfeeFreeRepository repository.ITransfeeFreeRepository
}

func NewTransportService(repository repository.ITransportRepository,
	transfeeRepository repository.ITransfeeRepository,
	transfeeFreeRepository repository.ITransfeeFreeRepository) ITransportService {
	return &TransportService{variables.GormDB(), repository, transfeeRepository, transfeeFreeRepository}
}

func (s *TransportService) FindById(ctx context.Context, id uint) (*model.Transport, error) {
	transport, err := s.repository.FindById(s.db, id)
	if err != nil {
		variables.Logger.Errorf(ctx, "findById transport failed: err %v, id %d", err, id)
		return nil, err
	}
	return transport, nil
}

func (s *TransportService) Find(ctx context.Context, shopId uint) ([]*model.Transport, error) {
	list, err := s.repository.Find(s.db, shopId)
	if err != nil {
		variables.Logger.Errorf(ctx, "find transport failed: err %v", err)
		return nil, err
	}
	return list, nil
}

func (s *TransportService) FindPage(ctx context.Context, paginator *pagehelper.Paginator) (*pagehelper.Record, error) {
	page, err := s.repository.FindPage(s.db, paginator.Params)
	if err != nil {
		variables.Logger.Errorf(ctx, "find transport to count failed: err %v, params %+v", err, paginator.Params)
		return paginator.Record, err
	}

	count, err := s.repository.Count(s.db, paginator.Params)
	if err != nil {
		variables.Logger.Errorf(ctx, "find transport to page: err: %v, params %+v", err, paginator.Params)
		return paginator.Record, err
	}

	return paginator.Result(page, count)
}

func (s *TransportService) CheckExist(ctx context.Context, transName string) (bool, error) {
	exist, err := s.repository.CheckExist(s.db, transName)
	if err != nil {
		variables.Logger.Errorf(ctx, "is checkExist failed: err %v, transName %s", err, transName)
		return exist, err
	}
	return exist, nil
}

func (s *TransportService) Create(ctx context.Context, req *request.TransportRequest) (id uint, err error) {
	return id, s.db.Transaction(func(tx *gorm.DB) error {
		// 插入运费模板
		transport := &model.Transport{
			TransName:        req.TransName,
			ShopId:           constant.SHOP_ID,
			ChargeType:       req.ChargeType,
			IsFreeFee:        req.IsFreeFee,
			HasFreeCondition: req.HasFreeCondition,
		}
		if err := s.repository.Create(tx, transport); err != nil {
			variables.Logger.Errorf(ctx, "create transport failed: err %v", err)
			return err
		}
		// 插入所有的运费项和城市
		if len(req.Transfees) > 0 {
			for i := 0; i < len(req.Transfees); i++ {
				req.Transfees[i].TransfeeId = transport.TransportId
			}

			if err := s.insertTransfeeAndTranscity(tx, req.Transfees, transport.TransportId); err != nil {
				variables.Logger.Errorf(ctx, "batch create insertTransfeeAndTranscity failed: err %v", err)
				return err
			}
		}

		// 插入所有的指定包邮条件项和城市
		if req.HasFreeCondition == 2 && len(req.TransfeeFrees) > 0 {
			if err := s.insertTransfeeFreeAndTranscityFree(tx, req.TransfeeFrees, transport.TransportId); err != nil {
				variables.Logger.Errorf(ctx, "batch create insertTransfeeFreeAndTranscityFree failed: err %v", err)
				return err
			}
		}

		*&id = transport.TransportId
		return nil
	})
}

func (s *TransportService) insertTransfeeAndTranscity(db *gorm.DB, transfeeFrees []request.Transfee, transportId uint) error {
	var transfees []*model.Transfee
	for _, transfeeFree := range transfeeFrees {
		item := &model.Transfee{
			TransportId:     transportId,
			ContinuousPiece: transfeeFree.ContinuousPiece,
			FirstPiece:      transfeeFree.FirstPiece,
			ContinuousFee:   transfeeFree.ContinuousFee,
			FirstFee:        transfeeFree.FirstFee,
		}

		if len(transfeeFree.CityList) > 0 {
			var cityList []model.Transcity
			for _, city := range transfeeFree.CityList {
				cityList = append(cityList, model.Transcity{
					CityId: city.AreaId,
				})
			}
			item.CityList = cityList
		}

		transfees = append(transfees, item)
	}

	return s.transfeeRepository.CreateBatch(db, transfees)
}

func (s *TransportService) insertTransfeeFreeAndTranscityFree(db *gorm.DB, free []request.TransfeeFree, transportId uint) error {
	var transfeeFrees []*model.TransfeeFree
	for _, transfeeFree := range free {
		item := &model.TransfeeFree{
			TransfeeFreeId: 0,
			TransportId:    transportId,
			FreeType:       transfeeFree.FreeType,
			Amount:         transfeeFree.Amount,
			Piece:          transfeeFree.Piece,
		}

		if len(transfeeFree.FreeCityList) > 0 {
			var freeCityList []model.TranscityFree
			for _, city := range transfeeFree.FreeCityList {
				freeCityList = append(freeCityList, model.TranscityFree{
					FreeCityId: city.AreaId,
				})
			}
			item.FreeCityList = freeCityList
		}

		transfeeFrees = append(transfeeFrees, item)
	}

	return s.transfeeFreeRepository.CreateBatch(db, transfeeFrees)
}

func (s *TransportService) Updates(ctx context.Context, req *request.TransportRequest) (id uint, err error) {
	transportId := req.TransportId
	//运费项
	transfees, err := s.FindTransfee(ctx, transportId)
	if err != nil && !errors.Is(gorm.ErrRecordNotFound, err) {
		return 0, err
	}

	//运费模板
	transfeeFrees, err := s.FindFransfeeFree(ctx, transportId)
	if err != nil && !errors.Is(gorm.ErrRecordNotFound, err) {
		return 0, err
	}

	return id, s.db.Transaction(func(tx *gorm.DB) error {
		// 删除所有的运费项
		if len(transfees) > 0 {
			err := s.transfeeRepository.DeleteBatch(tx, transfees)
			if err != nil {
				variables.Logger.Errorf(ctx, "batch delete transfee failed: err %v", err)
				return err
			}
		}

		// 删除运费模板
		if len(transfeeFrees) > 0 {
			err := s.transfeeFreeRepository.DeleteBatch(tx, transfeeFrees)
			if err != nil {
				variables.Logger.Errorf(ctx, "batch delete transfeeFree failed: err %v", err)
				return err
			}
		}

		// 删除关联关系
		err = s.repository.DeleteAssociation(tx, transfees, transfeeFrees)
		if err != nil {
			variables.Logger.Errorf(ctx, "batch delete transfees and transfeeFrees association failed: err %v", err)
			return err
		}

		// 更新运费模板
		transport := &model.Transport{
			TransportId:      req.TransportId,
			TransName:        req.TransName,
			ShopId:           constant.SHOP_ID,
			ChargeType:       req.ChargeType,
			IsFreeFee:        req.IsFreeFee,
			HasFreeCondition: req.HasFreeCondition,
		}
		err = s.repository.Updates(tx, transport)
		if err != nil {
			variables.Logger.Errorf(ctx, "updates transport failed: err %v", err)
			return err
		}

		// 插入所有的运费项和城市
		if len(req.Transfees) > 0 {
			for i := 0; i < len(req.Transfees); i++ {
				req.Transfees[i].TransfeeId = transport.TransportId
			}

			if err := s.insertTransfeeAndTranscity(tx, req.Transfees, transport.TransportId); err != nil {
				variables.Logger.Errorf(ctx, "batch create insertTransfeeAndTranscity failed: err %v", err)
				return err
			}
		}

		// 插入所有的指定包邮条件项和城市
		if req.HasFreeCondition == 2 && len(req.TransfeeFrees) > 0 {
			if err := s.insertTransfeeFreeAndTranscityFree(tx, req.TransfeeFrees, transport.TransportId); err != nil {
				variables.Logger.Errorf(ctx, "batch create insertTransfeeFreeAndTranscityFree failed: err %v", err)
				return err
			}
		}

		return nil
	})
}

func (s *TransportService) FindTransfee(ctx context.Context, transportId uint) (transfees []*model.Transfee, err error) {
	transfees, err = s.transfeeRepository.Find(s.db, &queryWrapper.WherePair{Query: "transport_id = ?", Args: []interface{}{transportId}})
	if err != nil && errors.Is(gorm.ErrRecordNotFound, err) {
		variables.Logger.Errorf(ctx, "find by transportId transfee failed: err %v", err)
		return nil, err
	}

	return transfees, nil
}

func (s *TransportService) FindFransfeeFree(ctx context.Context, transportId uint) (transfeeFrees []*model.TransfeeFree, err error) {
	transfeeFrees, err = s.transfeeFreeRepository.Find(s.db, &queryWrapper.WherePair{Query: "transport_id = ?", Args: []interface{}{transportId}})
	if err != nil && errors.Is(gorm.ErrRecordNotFound, err) {
		variables.Logger.Errorf(ctx, "find by transportId transfeeFrees failed: err %v", err)
		return nil, err
	}

	return transfeeFrees, nil
}

func (s *TransportService) DeleteById(ctx context.Context, transportId uint) error {
	//运费项
	transfees, err := s.FindTransfee(ctx, transportId)
	if err != nil && !errors.Is(gorm.ErrRecordNotFound, err) {
		return err
	}

	//运费模板
	transfeeFrees, err := s.FindFransfeeFree(ctx, transportId)
	if err != nil && !errors.Is(gorm.ErrRecordNotFound, err) {
		return err
	}

	return s.db.Transaction(func(tx *gorm.DB) error {
		// 删除所有运费项包含的城市
		err := s.repository.Delete(tx, transportId)
		if err != nil && !errors.Is(gorm.ErrRecordNotFound, err) {
			variables.Logger.Errorf(ctx, "delete by transportId  transport failed: err %v, transportId %d", err, transportId)
			return err
		}

		// 删除所有的运费项
		if len(transfees) > 0 {
			err := s.transfeeRepository.DeleteBatch(tx, transfees)
			if err != nil {
				variables.Logger.Errorf(ctx, "batch delete transfee failed: err %v", err)
				return err
			}
		}

		// 删除运费模板
		if len(transfeeFrees) > 0 {
			err := s.transfeeFreeRepository.DeleteBatch(tx, transfeeFrees)
			if err != nil {
				variables.Logger.Errorf(ctx, "batch delete transfeeFree failed: err %v", err)
				return err
			}
		}

		// 删除关联关系
		err = s.repository.DeleteAssociation(tx, transfees, transfeeFrees)
		if err != nil {
			variables.Logger.Errorf(ctx, "delete association failed: err %v", err)
			return err
		}

		return nil
	})
}
